﻿using System.Collections.Generic;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;

namespace ShevaEngine2ContentPipeline.Common.ModelSplit
{
    /// <summary>
    /// Class for splitting model.
    /// </summary>
    internal class MeshSplitter
    {
        /// <summary>Mesh object.</summary>
        internal MeshContent _Mesh;
        /// <summary>Mesh parts.</summary>
        private List<MeshSplitPart> _Parts;
        /// <summary>Max bones per part.</summary>
        internal int _MaxBones;
        /// <summary>Current index.</summary>
        internal int _CurrentIndex = 0;
        /// <summary>Meshes.</summary>
        private List<MeshContent> _Meshes;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="content">Mesh object.</param>
        /// <param name="maxBones">Maximal bones count.</param>        
        public MeshSplitter(MeshContent content, int maxBones)
        {
            this._Mesh = content;
            this._MaxBones = maxBones;
            this._Parts = new List<MeshSplitPart>();
        }

        /// <summary>
        /// Method splits model.
        /// </summary>
        /// <returns>Model parts.</returns>
        public List<MeshContent> Split()
        {
            this._Meshes = new List<MeshContent>();

            foreach (GeometryContent geom in _Mesh.Geometry)
                this._Split(geom);

            return _Meshes;
        }

        /// <summary>
        /// Specifies if model needs be splitted.
        /// </summary>
        /// <param name="mesh">Mesh node.</param>
        /// <param name="maxBones">Maximal bones count.</param>
        /// <returns></returns>
        public static bool NeedsSplitting(MeshContent mesh, int maxBones)
        {
            SortedDictionary<string, object> skinnedBones = new SortedDictionary<string, object>();

            foreach (GeometryContent geom in mesh.Geometry)
            {
                VertexChannel<BoneWeightCollection> weightChannel = null;
                foreach (VertexChannel channel in geom.Vertices.Channels)
                {
                    if (channel.Name == VertexChannelNames.Weights())
                    {
                        weightChannel = (VertexChannel<BoneWeightCollection>)channel;
                        break;
                    }
                }
                if (weightChannel != null)
                {
                    foreach (BoneWeightCollection weights in weightChannel)
                    {
                        foreach (BoneWeight weight in weights)
                        {
                            if (!skinnedBones.ContainsKey(weight.BoneName))
                                skinnedBones.Add(weight.BoneName, null);
                        }
                    }

                }

            }
            return skinnedBones.Keys.Count > maxBones;
        }

        /// <summary>
        /// Method splits geometry.
        /// </summary>
        /// <param name="geom"></param>
        private void _Split(GeometryContent geom)
        {
            int vertexStart = 0;
            MeshSplitPart part;
            while (vertexStart < geom.Indices.Count)
            {
                part = new MeshSplitPart(this, geom, vertexStart);


                _CurrentIndex++;
                MeshContent newMesh = part.Process();
                vertexStart = part.VertexEndIndex;
                newMesh.Transform = _Mesh.Transform;

                _Meshes.Add(newMesh);
            }
        }
    }
}
